scala wtf - 1 - implicit default parameters

wtf

# amm

@ def foo()(implicit a:Int=1, b:String="b") = { Console.println(s"a=$a; b=$b")}
defined function foo

@ foo()
a=1; b=b


@ { { implicit val x:Int=26; foo() } }
a=26; b=b

@ { { implicit val x:Int=26; foo()(b="dsa") } }
a=1; b=dsa

Boom - a is 1, wtf? I have been expecting 26.

Works other direction too:

@ { { implicit val x:String="asd"; foo()(a=26) } } 
a=26; b=b

I have been expecting b being "asd" in this case.

when

I wanted to use this one to specify one implicit parameter for particular function call explicitly, while grabbing other one implicitly or from default. As we can see this syntax grabs only default one.

why

Lets refer to scala spec: https://www.scala-lang.org/files/archive/spec/2.12/04-basic-declarations-and-definitions.html 4.6.1 explains it quite nicely - scala compiler basically generates extra methods. In our case we got

foo$default$1()                            // with a = 1,  b = "b"
foo$default$2()(implicit xA:Int=1)         // with a = xA, b = "b"
foo$default$3()(implicit xB:String="b")    // with a = 1,  b = xB

So when we call foo()(a=1) it unrolls into foo$default$2(xA=1)

workarounds

Provide implicit with very narrow scope and call without explicit specification of implicit params.

{ { implicit b:String="asd"; foo() } }

Lets see it in action with defaults

@ { { implicit val a:Int=26; foo() } } 
a=26; b=b

And now with implicit b in scope

@ implicit val b:String = "foo" 
b: String = "foo"

@ { { implicit val a:Int=26; foo() } } 
a=26; b=foo